﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Forms;

namespace BuildHelper.Work.Tools
{
    public class SystemSecurityOptions
    {
        public static bool HasOptions(string apps, string system)
        {
            string Hfile = apps + "\\" + system + @"\src\aix\secs\sec" + system + ".h";
            return File.Exists(Hfile);
        }
        public static SystemSecurityOptions GetSystemSecurityOptions(string apps, string system, string brancz)
        {
            string Hfile = apps + "\\" + system + @"\src\aix\secs\sec" + system + ".h";
            string GUIfile = apps + "\\" + system + @"\src\aix\secs\GUI" + system;
            string HfileD = apps + "\\" + system + @"\src\aix\secs\sec" + system + "d.h";
            string GUIfileD = apps + "\\" + system + @"\src\aix\secs\GUI" + system +"d";

            string SecOptions = apps + "\\" + system + @"\src\gui\" + system + "Tools\\" + system + "Security\\SecOptions.cs";
            string SecRegistration = apps + "\\" + system + @"\src\gui\" + system + "Tools\\" + system + "Security\\SecRegistration.cs";
            SystemSecurityOptions s = null;

            if (File.Exists(Hfile))
            {
                s = new SystemSecurityOptions();
                s.System = system;
                s.Brancz = brancz;
                string strTmp = string.Empty;
                #region GUI file
                strTmp = GUIfile.ReadFile();
                var list = strTmp.GetMatches(maskGUIFile, new int[] { 1, 2, 3 }, RegexOptions.IgnoreCase | RegexOptions.Multiline);
                List<string> l = new List<string>();
                List<string> l2 = new List<string>();
               
                foreach (var item in list)
                {
                    SecurityItem i = new SecurityItem();
                    i.GuiCode = item[3];
                    i.GuiName = item[2].Trim().TrimStart('\"').TrimEnd('\"');
                    i.ParentCode = item[1];
                    s.AllOptions.AddIfNotExist(i);
                }
                #endregion
                #region HFile
                strTmp = Hfile.ReadFile();
                
                list = strTmp.GetMatches(maskHFile, new int[] { 1, 2 }, RegexOptions.IgnoreCase | RegexOptions.Multiline);
                l.Clear();
                l2.Clear();
                foreach (var item in list)
                {
                    SecurityItem i = s.GetItemByGuiCode(item[1]);
                    if (i != null)
                        i.Number = item[2];
                    else
                        s.unusedHItems.Add(string.Format("{0} Number not used \"{1}\" [{2}]", s.System, item[2], item[1]));
                }
                #endregion
                #region GUI deleted file
                strTmp = GUIfileD.ReadFile();
                list = strTmp.GetMatches(maskGUIDFile, new int[] { 1, 2 }, RegexOptions.IgnoreCase | RegexOptions.Multiline);
                l.Clear();
                l2.Clear();
                foreach (var item in list)
                {
                    SecurityItem i = new SecurityItem();
                    i.GuiCode = item[2];
                    i.GuiName = item[1].Trim().TrimStart('\"').TrimEnd('\"');
                    s.DeletedOptions.AddIfNotExist(i);
                }
                #endregion
                #region H Delete File
                strTmp = HfileD.ReadFile();

                list = strTmp.GetMatches(maskHFile, new int[] { 1, 2 }, RegexOptions.IgnoreCase | RegexOptions.Multiline);
                l.Clear();
                l2.Clear();
                SecurityItem i2;
                foreach (var item in list)
                {
                    SecurityItem i = s.GetDeletedItemByGuiCode(item[1]);
                    if (i != null)
                    {
                        i.Number = item[2];
                        i2 = s.GetItemByNumber(i.Number);
                        if (i2 != null)
                            s.deletedItems.Add(string.Format("{0} Option \"{1}\" [{2}] Number: {3} will not be added as it is deleted.", s.System, i2.GuiName, i2.GuiCode, i2.Number));
                    }
                    else
                        s.unusedHDItems.Add(string.Format("{0} Number not used \"{1}\" [{2}]", s.System, item[2], item[1]));
                }
                #endregion

                #region SecRegistration
                strTmp = SecRegistration.ReadFile();
                list = strTmp.GetMatches(maskSRFile, new int[] { 2, 3 }, RegexOptions.IgnoreCase | RegexOptions.Multiline);

                foreach (var item in list)
                {
                    ClientSecurityItem ci = new ClientSecurityItem();
                    ci.Code = item[3];
                    ci.Params = item[2];
                    s.ClientOpts.Add(ci);
                }
                #endregion
                #region SecOptions
                strTmp = SecOptions.ReadFile();
                l.Clear();
                l2.Clear();
                list = strTmp.GetMatches(maskSOFile, new int[] { 1, 2, 4 }, RegexOptions.IgnoreCase | RegexOptions.Multiline);
                Dictionary<string, string> secoptdic = new Dictionary<string, string>();
                
                foreach (var item in list)
                {
                    List<ClientSecurityItem> ci = s.GetClientOptByCode(item[1]);
                    if (ci.Count > 0)
                    {
                        foreach (var cii in ci)
                        {
                            cii.Number = item[2];
                            cii.Name = item[4].Trim(' ','"');
                        }
                    }
                    else
                    {
                        s.unusedSOItems.Add(string.Format("{0} Number not used \"{1}\" [{2}]", s.System, item[2], item[1]));
                    }
                }
                #endregion

                foreach (var item in s.ClientOpts)
                {
                    SecurityItem i = s.GetItemByNumber(item.Number);
                    if (i != null)
                    {
                        i.SecOptCode = item.Code;
                        i.SecOptName = item.Name;
                        i.SecRegParams = item.Params;
                    }
                }
                s.CreateTree();
            }
            return s;
        }
        
        static string maskHFile = "^\\s*#define\\s*([\\w]*)\\s*(\\d*)\\s*(/\\*\\s*(.*?)\\s*\\*/)?";
        static string maskGUIFile = "^\\s*(\\w+)\\s*\"(.*?)\"\\s*(\\w*)";
        static string maskGUIDFile = "^\\s*-1\\s*\"(.*?)\"\\s*(\\w*)";
        static string maskSOFile = "^\\s*public\\s*const\\s*int\\s*(\\w*)\\s*=\\s*(\\d*)\\s*;\\s*(/\\*\\s*(.*?)\\s*\\*/)?";
        static string maskSRFile = "^\\s*(GcmTools\\.)?SecItem\\.SetItemOpt\\((.*?),\\s*SecOptions\\.(\\w*)\\s*\\);";

        public string System { get; private set; }
        public string Brancz { get; private set; }

        public List<SecurityItem> DeletedOptions { get; private set; }
        private List<ClientSecurityItem> ClientOpts = new List<ClientSecurityItem>();
        private List<ClientSecurityItem> GetClientOptByCode(string value)
        {
            List<ClientSecurityItem> l = new List<ClientSecurityItem>();
            foreach (ClientSecurityItem i in ClientOpts)
            {
                if (i.Code == value)
                    l.Add(i);
            }
            return l;
        }

        public List<SecurityItem> AllOptions { get; private set; }
        public List<SecurityItem> MainOptions { get; private set; }
        private NamedList unusedHItems = new NamedList("Brak użycia numeru z *.h");
        private NamedList unusedHDItems = new NamedList("Brak użycia kasowanego numeru z *d.h");
        private NamedList unusedSOItems = new NamedList("Brak użycia numeru z SecOptions.cs");
        private NamedList deletedItems = new NamedList("Opcje kasowane");

        public SecurityItem GetDeletedItemByGuiCode(string value)
        {
            foreach (var item in DeletedOptions)
                if (item.GuiCode == value)
                    return item;
            return null;
        }
        public SecurityItem GetItemByGuiCode(string value)
        {
            foreach (var item in AllOptions)
                if (item.GuiCode == value)
                    return item;
            return null;
        }
        public SecurityItem GetItemByNumber(string value)
        {
            foreach (var item in AllOptions)
                if (item.Number == value)
                    return item;
            return null;
        }
        public SecurityItem GetItemBySecOptCode(string value)
        {
            foreach (var item in AllOptions)
                if (item.SecOptCode == value)
                    return item;
            return null;
        }
        public SecurityItem GetItemByOptionName(SecurityItem option)
        {
            List<SecurityItem> items = new List<SecurityItem>();

            foreach (var item in AllOptions)
            {
                if (item.CompareByGuiName(option))
                    items.Add(item);
            }
            if(items.Count == 1)
                return items[0];
            if(items.Count > 1)
            {
                foreach (var i in items)
                {
                    if (i.GuiCode == option.GuiCode || i.Number == option.Number)
                        return i;
                }
                return items[0];
            }
            return null;
        }

        public SecurityItem GetItemByOptionNumber(SecurityItem option)
        {
            foreach (var item in AllOptions)
            {
                if (item.CompareByNumber(option))
                    return item;
            }
            return null;
        }
        public SecurityItem GetItemByOptionCode(SecurityItem option)
        {
            foreach (var item in AllOptions)
            {
                if (item.CompareByCode(option))
                    return item;
            }
            return null;
        }

        private SystemSecurityOptions()
        {
            AllOptions = new List<SecurityItem>();
            MainOptions = new List<SecurityItem>();
            DeletedOptions = new List<SecurityItem>();
        }
        private void CreateTree()
        {
            foreach (var item in AllOptions)
            {
                SecurityItem i = GetItemByGuiCode(item.ParentCode);
                if (i != null)
                {
                    if (i != item)
                    {
                        item.Parent = i;
                        i.ChildOptions.Add(item);
                    }
                }
            }
        }
        private void Compare(SystemSecurityOptions other, SecurityItem item, SecurityItem i, List<string> diffParams, List<string> diffNum)
        {
            if (i.Number != item.Number)
            {
                diffNum.Add(string.Format("{0} Differend numbers for option \"{1}\" {2}={3} [{4}], {5}={6} [{7}]",
                    System, item.FullPath, Brancz, item.Number, item.GuiCode, other.Brancz, i.Number, i.GuiCode));
            }
            if (i.SecRegParams != item.SecRegParams)
            {
                diffParams.Add(string.Format("{0} Differend parameters for option \"{1}\" {2}={3} [{4}], {5}={6} [{7}]",
                    System, item.FullPath, Brancz, item.SecOptCode, item.SecRegParams, other.Brancz, i.SecOptCode, i.SecRegParams));
            }
        }
        public List<NamedList> Compare(SystemSecurityOptions other)
        {
            List<NamedList> l = new List<NamedList>();

            NamedList list = new NamedList("Problems");
            NamedList diffParams = new NamedList("Different params");
            NamedList diffNum = new NamedList("Different numbers");
            NamedList NewOptWithOldNum = new NamedList("Nowe opcje ze starymi numerami");

            List<SecurityItem> used = new List<SecurityItem>();
            List<SecurityItem> missing = new List<SecurityItem>();
            List<SecurityItem> unused = new List<SecurityItem>();

            foreach (var item in AllOptions)
            {
                SecurityItem i = other.GetItemByOptionName(item);
                if (i == null)
                {
                    missing.Add(item);
                }
                else
                {
                    used.Add(i);
                    Compare(other, item, i, diffParams, diffNum);
                }
            }
            foreach (var i in other.AllOptions)
            {
                if (!used.Contains(i))
                    unused.Add(i);
            }
            List<SecurityItem> otherItems = new List<SecurityItem>();
            SecurityItem mathing = null;
            bool cancelall = false;
            int idx=0;
            using (Security.FormItemPick f = new Security.FormItemPick(unused, missing.Count))
            {
                foreach (var item in missing.ToArray())
                {
                    idx++;
                    otherItems.Clear();
                    mathing = null;
                    if (!cancelall)
                    {
                        if (item.Parent != null)
                        {
                            SecurityItem p = other.GetItemByOptionName(item.Parent);
                            if (p != null)
                            {
                                foreach (var i in p.ChildOptions)
                                {
                                    if (!used.Contains(i))
                                        otherItems.Add(i);
                                }
                            }
                            else
                            {
                                foreach (var i in other.AllOptions)
                                {
                                    if (!used.Contains(i))
                                        otherItems.Add(i);
                                }
                            }
                        }
                        else
                        {
                            foreach (var i in other.MainOptions)
                            {
                                if (!used.Contains(i))
                                    otherItems.Add(i);
                            }
                            foreach (var i in other.AllOptions)
                            {
                                if (!used.Contains(i) && !otherItems.Contains(i))
                                    otherItems.Add(i);
                            }
                        }
                        if (otherItems.Count > 0)
                        {
                            f.LoadItems(otherItems, item, idx);
                            f.ShowDialog();
                            mathing = f.Item;
                            if (f.CancelAll)
                                cancelall = true;
                        }
                    }
                    if (mathing != null)
                    {
                        used.Add(mathing);
                        unused.Remove(mathing);
                        Compare(other, item, mathing, diffParams, diffNum);
                    }
                    else
                    {
                        list.Add(string.Format("{0} Missing option \"{1}\", Code: [{2}], Number: [{3}]", System, item.FullPath, item.GuiCode, item.Number));
                    }
                }
                f.SaveLayout();
            }
            SecurityItem oldSi;
            foreach (var item in unused)
            {
                if (item.Number.IsNotNull())
                {
                    oldSi = GetItemByNumber(item.Number);
                    if (oldSi != null)
                    {
                        NewOptWithOldNum.Add(string.Format("{0} New Option in {6}: \"{1}\", Code: [{2}] Added with old Number: [{3}] Old Name:\"{4}\" Old Code: [{5}]", System, item.FullPath, item.GuiCode, item.Number, oldSi.FullPath, oldSi.GuiCode, other.Brancz));
                    }
                }
            }
            l.Add(list);
            l.Add(diffNum);
            l.Add(diffParams);
            l.Add(NewOptWithOldNum);
            return l;
        }
        public List<NamedList> Check()
        {
            List<NamedList> list = new List<NamedList>();
            NamedList notRegisteredOnClient = new NamedList("Braki w *.h");
            NamedList SecRegistrationBraki = new NamedList("Braki w SecRegistration.cs");
            DuplicateCheck<SecurityItem> SecRegParamsDuplicateCheck = new DuplicateCheck<SecurityItem>();
            DuplicateCheck<SecurityItem> NumberCheck = new DuplicateCheck<SecurityItem>();
            DuplicateCheck<SecurityItem> CodeCheck = new DuplicateCheck<SecurityItem>();
            DuplicateCheck<ClientSecurityItem> NumberCheckClient = new DuplicateCheck<ClientSecurityItem>();
            DuplicateCheck<ClientSecurityItem> CodeCheckClient = new DuplicateCheck<ClientSecurityItem>();
            DuplicateCheck<SecurityItem> FullNameDup = new DuplicateCheck<SecurityItem>();
            
            foreach (var item in AllOptions)
            {
                if (item.SecRegParams.IsNull())
                    SecRegistrationBraki.Add(string.Format("{0} Option \"{1}\" Gui Code: {2}, Number: {3}, SecOption Code:{4} not registered in SecRegistration.cs:", System, item.FullPath, item.GuiCode, item.Number, item.SecOptCode));
                else
                    SecRegParamsDuplicateCheck.Add(item, item.SecRegParams);

                if (item.Number.IsNull())
                    notRegisteredOnClient.Add(string.Format("{0} No number for option \"{1}\" [{2}]", System, item.FullPath, item.GuiCode));
                else
                    NumberCheck.Add(item, item.Number);
               
                if (item.GuiCode.IsNotNull())
                    CodeCheck.Add(item, item.GuiCode);

                FullNameDup.Add(item, item.FullPath);
            }

            NamedList ClientOptNotRegistered = new NamedList("Braki w GUIXXX");
            NamedList NoNum = new NamedList("Braki w SecOptions.cs");
            
            foreach (var item in ClientOpts)
            {
                if (item.Number.IsNotNull())
                {
                    NumberCheckClient.Add(item, item.Number);
                    if (item.Code.IsNotNull())
                        CodeCheckClient.Add(item, item.Code);

                    if (GetItemByNumber(item.Number) == null)
                        ClientOptNotRegistered.Add(string.Format("{0} Option {1} [{2}] \"{3}\" registered in SecRegistration is missing in GUI file", System, item.Code, item.Number, item.Name));
                }
                else
                {
                    NoNum.Add(string.Format("{0} No number for option \"{1}\"", System, item.Code));
                }
            }
            string tmp;
            NamedList DuplicatedParams = new NamedList("Duplikaty w SecRegistration.cs");
            
            foreach (var item in SecRegParamsDuplicateCheck.Duplicates)
            {
                tmp = string.Empty;
                foreach(var i in item.Value)
                    tmp = tmp.AppendLine(string.Format("\t Option:{0}, GUIXXX Code:{1}, Number:{2}, SecOptions Code:{3}", i.FullPath, i.GuiCode, i.Number, i.SecOptCode));
                tmp = string.Format("{0} Duplicate registration parameters: {1}", System, item.Key).AppendLine(tmp);
                DuplicatedParams.Add(tmp);
            }
            DuplicatedParams.Add("");
            foreach (var item in CodeCheckClient.Duplicates)
            {
                tmp = string.Empty;
                foreach (var i in item.Value)
                    tmp = tmp.AppendLine(string.Format("\t Params:{0}", i.Params));
                tmp = string.Format("{0} Duplicate code registration: {1}, Name:{2}, Number:{3}", System, item.Key, item.Value[0].Name, item.Value[0].Number).AppendLine(tmp);
                DuplicatedParams.Add(tmp);
            }
            NamedList DuplicatedNumber = new NamedList("Duplikaty w *.h");

            foreach (var item in NumberCheck.Duplicates)
            {
                tmp = string.Empty;
                foreach (var i in item.Value)
                    tmp = tmp.AppendLine(string.Format("\t Option:{0}, GUIXXX Code:{1}, Number:{2}, SecOptions Code:{3}", i.FullPath, i.GuiCode, i.Number, i.SecOptCode));
                tmp = string.Format("{0} Duplicate number: {1}", System, item.Key).AppendLine(tmp);
                DuplicatedNumber.Add(tmp);
            }


            NamedList DuplicatedCode = new NamedList("Duplikaty w GUIXXX");

            foreach (var item in CodeCheck.Duplicates)
            {
                tmp = string.Empty;
                foreach (var i in item.Value)
                    tmp = tmp.AppendLine(string.Format("\t Option:{0}, GUIXXX Code:{1}, Number:{2}, SecOptions Code:{3}", i.FullPath, i.GuiCode, i.Number, i.SecOptCode));
                tmp = string.Format("{0} Duplicate code: {1}", System, item.Key).AppendLine(tmp);
                DuplicatedCode.Add(tmp);
            }
            DuplicatedCode.Add("");
            foreach (var item in FullNameDup.Duplicates)
            {
                tmp = string.Empty;
                foreach (var i in item.Value)
                    tmp = tmp.AppendLine(string.Format("\t Option: GUIXXX Code:{0}, Number:{1}, SecOptions Code:{2}", i.GuiCode, i.Number, i.SecOptCode));
                tmp = string.Format("{0} Duplicate option: {1}", System, item.Key).AppendLine(tmp);
                DuplicatedCode.Add(tmp);
            }


            NamedList DuplicatedNumClient = new NamedList("Duplikaty w SecOptions.cs");

            foreach (var item in NumberCheckClient.Duplicates)
            {
                if (IsCodeTheSame(item.Value))
                    continue;
                tmp = string.Empty;
                foreach (var i in item.Value)
                    tmp = tmp.AppendLine(string.Format("\t Option: Code:{0}, Name:{1}", i.Code, i.Name));
                tmp = string.Format("{0} Duplicate number: {1}", System, item.Key).AppendLine(tmp);
                DuplicatedNumClient.Add(tmp);
            }


            list.Add(SecRegistrationBraki);
            list.Add(notRegisteredOnClient);
            list.Add(ClientOptNotRegistered);
            list.Add(NoNum);
            list.Add(DuplicatedParams);
            list.Add(DuplicatedNumber);
            list.Add(DuplicatedCode);
            list.Add(DuplicatedNumClient);
            list.Add(unusedHItems);
            list.Add(unusedHDItems); 
            list.Add(unusedSOItems);
            list.Add(deletedItems);
            return list;
        }
        private bool IsCodeTheSame(List<ClientSecurityItem> list)
        {
            List<string> codes = new List<string>();
            foreach (ClientSecurityItem i in list)
            {
                codes.AddIfNotExist(i.Code);
            }
            return codes.Count < 2;
        }
        private bool IsDuplicated(string value, List<string> l)
        {
            if (l.Contains(value))
                return true;
            l.Add(value);
            return false;
        }
        private class DuplicateCheck<T>
        {
            public Dictionary<string, T> Items = new Dictionary<string, T>();
            public Dictionary<string, List<T>> Duplicates = new Dictionary<string, List<T>>();
            public void Add(T item, string key)
            {
                if (Items.ContainsKey(key))
                {
                    List<T> l = null;
                    if (!Duplicates.ContainsKey(key))
                    {
                        l = new List<T>();
                        l.Add(Items[key]);
                        Duplicates.Add(key, l);
                    }
                    else
                    {
                        l = Duplicates[key];
                    }
                    l.Add(item);
                }
                else
                    Items.Add(key, item);
            }
        }

    }

    public class SecurityItem
    {
        List<SecurityItem> _ChildOptions = new List<SecurityItem>();
        public string GuiCode { get; set; }
        public string ParentCode { get; set; }
        public SecurityItem Parent { get; set; }
        
        public string ParentGuiCode
        {
            get
            {
                if (Parent != null) return Parent.GuiCode;
                return null;
            }
        }
        public string ParentGuiName
        {
            get
            {
                if (Parent != null) return Parent.GuiName;
                return null;
            }
        }
        public string ParentNumber
        {
            get
            {
                if (Parent != null) return Parent.Number;
                return null;
            }
        }

        public List<SecurityItem> ChildOptions { get { return _ChildOptions; } }
        public string GuiName { get; set; }
        public string Number { get; set; }
        public string SecOptCode { get; set; }
        public string SecOptName { get; set; }
        public string SecRegParams { get; set; }
        public bool CompareByGuiName(SecurityItem option)
        {
            if (this.GuiName == option.GuiName)
            {
                if (Parent == null && option.Parent == null)
                    return true;
                else if (Parent != null && option.Parent != null && Parent.CompareByGuiName(option.Parent))
                    return true;
            }
            return false;
        }
        public bool CompareByNumber(SecurityItem option)
        {
            if (this.Number == option.Number)
            {
                if (Parent == null && option.Parent == null)
                    return true;
                else if (Parent != null && option.Parent != null && Parent.CompareByNumber(option.Parent))
                    return true;
            }
            return false;
        }
        public bool CompareByCode(SecurityItem option)
        {
            if (this.GuiCode == option.GuiCode)
            {
                if (Parent == null && option.Parent == null)
                    return true;
                else if (Parent != null && option.Parent != null && Parent.CompareByCode(option.Parent))
                    return true;
            }
            return false;
        }

        public string GuiString
        {
            get { return GuiName.AddDoubleQuotes() + " " + GuiCode.PutBetween("[", "]"); }
        }
        public string GuiNumString
        {
            get { return GuiName.AddDoubleQuotes() + " " + GuiCode.PutBetween("[", "]") + " " + Number.PutBetween("[", "]"); }
        }
        public string FullPath
        {
            get 
            {
                string s = GuiName;
                SecurityItem p = Parent;
                while (p != null)
                {
                    s = p.GuiName + "->" + s;
                    p = p.Parent;
                }
                return s;
            }
        }
    }
    public class ClientSecurityItem
    {
        public string Code { get; set; }
        public string Name { get; set; }
        public string Number { get; set; }
        public string Params { get; set; }
    }
    public class NamedList : List<string>
    {
        public string Name { get; set; }
        public NamedList(string name)
        {
            Name = name;
        }
    }
}
